Runcontrol Setup for Application Exe
Description
This document describes how to configure a stream that runs a Windows executable in a RunControl batch.
Note: these steps apply to RunControl 5.3.x only. From 5.5.4 onwards do not do any of this — configure the executable from the App Batch option on the Stream Definition screen instead.
On 5.3.x there is no Stream Definition screen in the application, so a stream is created by writing two rows directly into the RunControl database — one in STREAMDEF holding the stream definition, and one in BATCHSTREAM mapping that stream to a batch and holding its dependencies.
Step 1 — Pick the ExecutorId from ExecutorDef
-- SQL Server
SELECT ExecutorId, ExecutorUrl
FROM ExecutorDef;
-- Oracle
SELECT "ExecutorId", "ExecutorUrl"
FROM "ExecutorDef";
How to choose: pick the executor id for the same Windows server URL where the .exe is deployed.
Step 2 — Identify the batch and pick the BATCHID from BATCHDEF
- In the DH application, identify the batch name. Note the batch name exactly as displayed.
- Look that name up in
BATCHDEF:
-- SQL Server
SELECT BATCHID, BATCHNAME
FROM BATCHDEF
WHERE BATCHNAME = 'your batch name';
-- Oracle
SELECT "BATCHID", "BATCHNAME"
FROM "BATCHDEF"
WHERE "BATCHNAME" = 'your batch name';
Step 3 — Decide the new STREAMID
3.1 Check the stream IDs present for that batch
-- SQL Server (example: BATCHID = 4)
SELECT bs.BATCHID, bs.STREAMID, sd.STREAMNAME
FROM BATCHSTREAM bs
JOIN STREAMDEF sd ON sd.STREAMID = bs.STREAMID
WHERE bs.BATCHID = 4
ORDER BY bs.STREAMID;
-- Oracle
SELECT bs."BATCHID", bs."STREAMID", sd."STREAMNAME"
FROM "BATCHSTREAM" bs
JOIN "STREAMDEF" sd ON sd."STREAMID" = bs."STREAMID"
WHERE bs."BATCHID" = 4
ORDER BY bs."STREAMID";
3.2 Pick the new STREAMID
Keep the batch number as the prefix. A stream's ID should begin with the BATCHID it belongs to, so the
ID itself tells you which batch it is in. For BATCHID = 4 the stream IDs look like 4001, 4006, 4014.
Then pick the number according to where we want the stream to sit:
| What you want | What to use | Example — BATCHID 4, existing streams 4001, 4006, 4014 |
|---|---|---|
| The new stream at the end of the batch | MAX(STREAMID) + 4 for that batch | 4014 + 4 = 4018 |
| The new stream between two existing streams | any free number between the two | between 4006 and 4014 → 4010 |
Step 4 — Build the stream definition JSON
This is the JSON that goes into STREAMDEF.STREAMDESC.
4.1 Structure
STREAMDESC
└── streamName, streamId
└── flows[]
├── flowId, name, executorID, flowDependencies[]
└── process[]
├── processId, processName
├── processBinary
├── processArguments[]
├── processDependencies[]
├── processType
├── infoLog, errorLog, processReport
4.2 Field reference
Root
| Field | Description |
|---|---|
streamName | Stream name. Max 25 chars, must match STREAMDEF.STREAMNAME |
streamId | The STREAMID from Step 3, quoted — "4018" |
flows | Array — normally one flow |
flows[]
| Field | Description |
|---|---|
flowId | "1" |
name | Name of the flow |
executorID | ExecutorId from Step 1, quoted — "1" |
flowDependencies | [] for a single-flow stream |
process | Array — one entry per program |
process[]
| Field | Description |
|---|---|
processId | "1", "2", … numbering starts at 1 |
processName | Unique, readable name |
processBinary | Full path to the .exe |
processArguments | Arguments — one array element per token |
processDependencies | processIds that must finish first; [] = starts immediately |
processType | "OTHER" for a customer exe |
infoLog | Full path of the normal log file |
errorLog | Full path of the error log file |
processReport | "" for a customer exe |
⚠️ Backslashes: in JSON a single
\does not work — always double it.Z:\DH\App.exemust be written as"Z:\\DH\\App.exe". This applies to every path —processBinary,infoLog,errorLog,processReport, and any path insideprocessArguments.
4.3 Example — one Windows exe in a stream
{
"streamName": "GL Recon Utility",
"streamId": "4018",
"flows": [
{
"flowId": "1",
"name": "gl_recon_flow",
"flowDependencies": [],
"executorID": "1",
"process": [
{
"processId": "1",
"processName": "gl_recon_util",
"processBinary": "Z:\\DEMO\\DH\\SH_PROGRAMS\\GLReconUtility.exe",
"processArguments": [
"-mode",
"EOD",
"-input",
"Z:\\DEMO\\DH\\SH_DATA\\B_4\\gl_input.txt",
"-output",
"Z:\\DEMO\\DH\\SH_DATA\\B_4\\gl_output.txt"
],
"processDependencies": [],
"processReport": "",
"processType": "OTHER",
"infoLog": "Z:\\DEMO\\DH\\SH_LOGS\\B_4\\log_gl_recon_util.txt",
"errorLog": "Z:\\DEMO\\DH\\SH_LOGS\\B_4\\diag_log_gl_recon_util.txt"
}
]
}
]
}
4.4 Example — two exes, second waits for the first
Only the process array changes:
"process": [
{
"processId": "1",
"processName": "gl_extract",
"processBinary": "Z:\\DEMO\\DH\\SH_PROGRAMS\\GLExtract.exe",
"processArguments": ["-date", "EOD"],
"processDependencies": [],
"processReport": "",
"processType": "OTHER",
"infoLog": "Z:\\DEMO\\DH\\SH_LOGS\\B_4\\log_gl_extract.txt",
"errorLog": "Z:\\DEMO\\DH\\SH_LOGS\\B_4\\diag_log_gl_extract.txt"
},
{
"processId": "2",
"processName": "gl_recon_util",
"processBinary": "Z:\\DEMO\\DH\\SH_PROGRAMS\\GLReconUtility.exe",
"processArguments": ["-mode", "EOD"],
"processDependencies": ["1"],
"processReport": "",
"processType": "OTHER",
"infoLog": "Z:\\DEMO\\DH\\SH_LOGS\\B_4\\log_gl_recon_util.txt",
"errorLog": "Z:\\DEMO\\DH\\SH_LOGS\\B_4\\diag_log_gl_recon_util.txt"
}
]
4.5 Validate the JSON before it goes near the database
- Paste it into a JSON validator (VS Code, Notepad++ JSON plugin, any validator).
- Confirm: no trailing commas, every string quoted, every backslash doubled.
- Confirm
streamIdinside the JSON is the same number you will insert intoSTREAMDEF.STREAMID.
Step 5 — Build the dependency JSON
This is the JSON that goes into BATCHSTREAM.STREAMDEP. It lists the streams that must finish before
this one starts.
{ "dependencies": [] }
{ "dependencies": [110, 120] }
- Empty list → the stream can start as soon as the batch starts.
- The IDs are StreamIds (numbers, not quoted), and they must belong to the same batch.
- Do not create a loop (A waits for B and B waits for A) — the batch will never start.
Step 6 — Insert the rows
Both JSON documents are now ready — STREAMDESC from Step 4 and STREAMDEP from Step 5. Insert into
STREAMDEF first, then BATCHSTREAM.
Note: always name the columns. STREAMDEF's column order is STREAMID, STREAMDESC, STREAMNAME — the
binary column is second and the name third, which is the opposite of what most people assume.
6.1 SQL Server
BEGIN TRANSACTION;
DECLARE @StreamId INT = 4018; -- from Step 3
DECLARE @BatchId INT = 4; -- from Step 2
DECLARE @StreamName VARCHAR(25) = 'GL Recon Utility';
-- NOTE: VARCHAR, not NVARCHAR — NVARCHAR stores the text as UTF-16, which DH cannot read
DECLARE @StreamDesc VARCHAR(MAX) = '<<paste the validated JSON from Step 4 here>>';
DECLARE @StreamDep VARCHAR(MAX) = '{ "dependencies": [] }'; -- from Step 5
-- 1) the stream definition
INSERT INTO STREAMDEF (STREAMID, STREAMNAME, STREAMDESC)
VALUES (@StreamId, @StreamName, CONVERT(VARBINARY(MAX), @StreamDesc));
-- 2) map the stream to the batch
INSERT INTO BATCHSTREAM (BATCHID, STREAMID, STREAMDEP, IsActive)
VALUES (@BatchId, @StreamId, CONVERT(VARBINARY(MAX), @StreamDep), 'Y');
-- verify before committing
SELECT sd.STREAMID, sd.STREAMNAME, bs.BATCHID, bs.IsActive,
CONVERT(VARCHAR(MAX), bs.STREAMDEP) AS StreamDepText,
CONVERT(VARCHAR(MAX), sd.STREAMDESC) AS StreamDescText
FROM STREAMDEF sd
JOIN BATCHSTREAM bs ON bs.STREAMID = sd.STREAMID
WHERE sd.STREAMID = @StreamId;
-- COMMIT; -- uncomment only after the SELECT above looks correct
Note: the JSON can also be inlined directly, without the variable — CONVERT(VARBINARY(MAX), '<json>').
If you do, use a plain literal and not N'...', and double every single quote inside the JSON.
6.2 Oracle
STREAMDESC is a BLOB NOT NULL, and the JSON is normally longer than a single SQL literal allows, so it is
built up in a PL/SQL block. Paste the JSON in pieces — each APPEND literal under 4000 characters, as
many lines as you need. Split it anywhere; the pieces are simply joined end to end.
DECLARE
v_StreamId NUMBER := 4018; -- from Step 3
v_BatchId NUMBER := 4; -- from Step 2
v_StreamName VARCHAR2(25) := 'GL Recon Utility';
v_DescClob CLOB;
v_DepClob CLOB;
v_DescBlob BLOB;
v_DepBlob BLOB;
v_dest_offset INTEGER := 1;
v_src_offset INTEGER := 1;
v_lang_context INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
v_warning INTEGER;
BEGIN
-- 1) the STREAMDESC JSON from Step 4, in chunks of under 4000 characters
DBMS_LOB.CREATETEMPORARY(v_DescClob, TRUE);
DBMS_LOB.APPEND(v_DescClob, TO_CLOB('<<paste chunk 1 of the JSON here>>'));
DBMS_LOB.APPEND(v_DescClob, TO_CLOB('<<paste chunk 2 of the JSON here>>'));
-- ... add as many APPEND lines as the JSON needs
-- 2) the STREAMDEP JSON from Step 5
v_DepClob := TO_CLOB('{ "dependencies": [] }');
-- 3) CLOB -> BLOB
DBMS_LOB.CREATETEMPORARY(v_DescBlob, TRUE);
DBMS_LOB.CONVERTTOBLOB(v_DescBlob, v_DescClob, DBMS_LOB.LOBMAXSIZE,
v_dest_offset, v_src_offset,
DBMS_LOB.DEFAULT_CSID, v_lang_context, v_warning);
v_dest_offset := 1;
v_src_offset := 1;
v_lang_context := DBMS_LOB.DEFAULT_LANG_CTX;
DBMS_LOB.CREATETEMPORARY(v_DepBlob, TRUE);
DBMS_LOB.CONVERTTOBLOB(v_DepBlob, v_DepClob, DBMS_LOB.LOBMAXSIZE,
v_dest_offset, v_src_offset,
DBMS_LOB.DEFAULT_CSID, v_lang_context, v_warning);
-- 4) the stream definition
INSERT INTO "STREAMDEF" ("STREAMID", "STREAMNAME", "STREAMDESC")
VALUES (v_StreamId, v_StreamName, v_DescBlob);
-- 5) map the stream to the batch (note: ISACTIVE is upper case on this table)
INSERT INTO "BATCHSTREAM" ("BATCHID", "STREAMID", "STREAMDEP", "ISACTIVE")
VALUES (v_BatchId, v_StreamId, v_DepBlob, 'Y');
END;
/
-- verify before committing
SELECT sd."STREAMID", sd."STREAMNAME", bs."BATCHID", bs."ISACTIVE",
UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(bs."STREAMDEP", 2000, 1)) AS "StreamDepText",
UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(sd."STREAMDESC", 2000, 1)) AS "StreamDescHead"
FROM "STREAMDEF" sd
JOIN "BATCHSTREAM" bs ON bs."STREAMID" = sd."STREAMID"
WHERE sd."STREAMID" = 4018;
-- COMMIT; -- run only after the SELECT above looks correct
Note:
- Oracle opens a transaction on its own — there is no
BEGIN TRANSACTION. Nothing is permanent until youCOMMIT, andROLLBACK;undoes the whole block. DBMS_LOB.SUBSTRreturns the first 2000 bytes only, soStreamDescHeadshows the start of the JSON. That is enough to confirm it is readable and that the backslashes survived.STREAMDEPis only a few dozen bytes, so theBATCHSTREAMinsert can be run on its own as a plain statement instead —UTL_RAW.CAST_TO_RAW('{ "dependencies": [] }'). Do not useUTL_RAW.CAST_TO_RAWforSTREAMDESC: aRAWvalue is limited to 2000 bytes in SQL and a string literal to 4000, and a stream definition normally exceeds both.
Common failures and what they usually mean
| Symptom | Most likely cause |
|---|---|
| Stream does not appear in the batch at all | BATCHSTREAM.IsActive (Oracle: ISACTIVE) is not 'Y', or the row was inserted against the wrong BATCHID |
| Stream appears but never starts | It is waiting on a STREAMDEP entry that never completes — or a circular dependency |
| Stream fails instantly, no log file | Bad processBinary path (usually a single backslash), exe not on that executor, or the log folder does not exist |
| Oracle rejects the insert with a check-constraint error | Some environments carry an IS JSON check on BATCHSTREAM.STREAMDEP — your STREAMDEP is not valid JSON |
| Oracle rejects the insert with a foreign-key error | The STREAMDEF row was not inserted first, or the BATCHID does not exist |
Oracle raises ORA-01704 or ORA-06502 on insert | The JSON was inlined as a literal instead of built as a CLOB — use the PL/SQL block in Step 6.2 |
STREAMDESC reads back as ????? or unreadable | Stored as UTF-16 — on SQL Server, declare the variable as VARCHAR(MAX) and not NVARCHAR(MAX) |
Rollback
Delete in the reverse order of insertion, inside a transaction:
SQL Server
BEGIN TRANSACTION;
DELETE FROM BATCHSTREAM WHERE STREAMID = <your streamid>;
DELETE FROM STREAMDEF WHERE STREAMID = <your streamid>;
-- COMMIT;
Oracle
DELETE FROM "BATCHSTREAM" WHERE "STREAMID" = <your streamid>;
DELETE FROM "STREAMDEF" WHERE "STREAMID" = <your streamid>;
-- COMMIT; -- or ROLLBACK; to undo
Note: if the batch has already run with this stream, do not delete — set
BATCHSTREAM.IsActive = 'N' (Oracle: ISACTIVE) instead, so the run history stays intact.